package com.gwtprocessingjs.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Random;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.RootPanel;

/**
 * This class demonstrates how to create a simple color picker by
 * using Processing code written in Javascript.
 */
public class JsColorPicker implements EntryPoint {
	
	// UI Elements.
	private final FlowPanel back = new FlowPanel();
	
	// 
	private static final ColorPicker colorPicker = new ColorPicker();
		
	@Override
	public void onModuleLoad() {
		
		// Note: methods used by the Javascript must be exported
		// before the canvas is initialized or the Processing rendering
		// loop will crash as it attempts to call missing methods.
		exportColorMethods();
		initializeCanvas();
		
		back.add(colorPicker);		
		back.add(new HTML("Watch this processing.js-based color " + 
				"picker communicate with a different canvas!"));
		RootPanel.get("gwt").add(back);
	}
	
	// --- Public static methods that allow the JS to call into GWT ---
	
	/** @return the red channel value of the color picker */
	public static int getRed() {
		return colorPicker.getRed();
	}
	
	/** @return the green channel value of the color picker */
	public static int getGreen() {
		return colorPicker.getGreen();
	}
	
	/** @return the blue channel value of the color picker */
	public static int getBlue() {
		return colorPicker.getBlue();
	}
	
	// --- Private Methods ---
	
	/**
	 * Initialize an instance of Processing and bind it to the
	 * canvas element defined in the HTML file.
	 */
	private native void initializeCanvas() /*-{
		$wnd.initializeCanvas();
	}-*/;
	
	/**
	 * This method exports methods on this class into global variables
	 * in Javascript. See this for more info:
	 * http://code.google.com/webtoolkit/doc/latest/DevGuideCodingBasicsJSNI.html#calling
	 */
	private native void exportColorMethods() /*-{
		
		// Bind the static getRed method on this class to
		// a variable in $wnd named "getRed". Same for G and B.
		$wnd.getRed =
          $entry(@com.gwtprocessingjs.client.JsColorPicker::getRed());
        $wnd.getGreen =
          $entry(@com.gwtprocessingjs.client.JsColorPicker::getGreen());
        $wnd.getBlue =
          $entry(@com.gwtprocessingjs.client.JsColorPicker::getBlue());
	}-*/;


	/**
	 * GWT color picker Widget that uses a dynamically generated
	 * canvas element and Processing Javascript code to render.
	 */
	private static class ColorPicker extends Composite {
		
		// UI Elements.
		private final FlowPanel back = new FlowPanel();
		private final HTML canvasElement = new HTML();
		
		// --- Constants ---
		
		// Width of the saturation / value square on the left.
		private static final int SV_WIDTH = 150;
		
		// Width of the hue bar on the right.
		private static final int HUE_WIDTH = 30;
		
		// Height of the main body of the control.
		private static final int BODY_HEIGHT = 150;
		
		// Height of the selected color swatch on the bottom of the control.
		private static final int SWATCH_HEIGHT = 30;
		
		// Initial values for the hue and value controls.
		private static final int HUE_R = 255;
		private static final int HUE_G = 255;
		private static final int HUE_B = 255;
		private static final int VALUE_R = 255;
		private static final int VALUE_G = 255;
		private static final int VALUE_B = 255;
		
		// ID for the canvas for this picker.
		private final String CANVAS_ID;
				
		public ColorPicker() {
			
			// Generate a random canvas ID so that you could potentially
			// support multiple color pickers.
			CANVAS_ID = "ColorPickerCanvas" + Random.nextInt();
			canvasElement.setHTML("<canvas id=\"" + CANVAS_ID + "\"/>");
			back.add(canvasElement);	
			initWidget(back);
		}
		
		/** @return the red channel value. */
		public int getRed() {
			return getRed(CANVAS_ID);
		}
		
		/** @return the green channel value. */
		public int getGreen() {
			return getGreen(CANVAS_ID);
		}
		
		/** @return the blue channel value. */
		public int getBlue() {
			return getBlue(CANVAS_ID);
		}
		
		@Override
		protected void onAttach() {
			super.onAttach();
			
			// The processing cannot be initialized until the canvas
			// is attached to the page by GWT.
			initializeCanvas(CANVAS_ID,
					SV_WIDTH, HUE_WIDTH, BODY_HEIGHT, SWATCH_HEIGHT,
					HUE_R, HUE_G, HUE_B, VALUE_R, VALUE_G, VALUE_B);
		}
		
		// --- Private Methods ---
		
		/**
		 * Creates the processing instance and associates
		 * it with the Canvas element.
		 */
		private native void initializeCanvas(String canvasId,
				int svWidth, int hueWidth, int bodyHeight, int swatchHeight,
				int hueR, int hueG, int hueB,
				int valueR, int valueG, int valueB) /*-{
			$wnd.initializeColorPickerCanvas(canvasId, svWidth, hueWidth, bodyHeight,
				swatchHeight, hueR, hueG, hueB, valueR, valueG, valueB);
		}-*/;
		
		
		/**
		 * @param canvasId id of the canvas to query.
		 * @return the red channel value
		 */
		private native int getRed(String canvasId) /*-{
			return $wnd.getRedValue(canvasId);
		}-*/;
		
		/**
		 * @param canvasId id of the canvas to query.
		 * @return the green channel value
		 */
		private native int getGreen(String canvasId) /*-{
			return $wnd.getGreenValue(canvasId);
		}-*/;
		
		/**
		 * @param canvasId id of the canvas to query.
		 * @return the blue channel value
		 */
		private native int getBlue(String canvasId) /*-{
			return $wnd.getBlueValue(canvasId);
		}-*/;
	}
}
